package com.pro.common.service.file.service;

import cn.hutool.json.JSONUtil;
import com.pro.common.api.CommonConst;
import com.pro.common.api.model.BaseModel;
import com.pro.common.api.statics.user.amount.enums.EnumAmountNegativeDeal;
import com.pro.common.api.statics.user.amount.model.base.AmountEntityRecordBaseDTO;
import com.pro.common.api.statics.user.amount.model.base.AmountEntityUnitDTO;
import com.pro.common.api.statics.user.file.storage.service.IFileStorageService;
import com.pro.common.api.user.file.enums.EnumFileNodeType;
import com.pro.common.api.user.file.model.db.UserFile;
import com.pro.common.api.user.file.model.db.UserFileNode;
import com.pro.common.api.user.intf.UserAmountFeignClient;
import com.pro.common.api.user.intf.UserBatchInfoFeignClient;
import com.pro.common.api.user.model.db.UserAmountRecord;
import com.pro.common.api.user.model.db.UserBatchDetail;
import com.pro.common.api.user.model.db.UserBatchInfo;
import com.pro.common.service.file.dao.UserFileNodeDao;
import com.pro.framework.api.FrameworkConst;
import com.pro.framework.api.database.TimeQuery;
import com.pro.framework.api.database.page.IPageInput;
import com.pro.framework.api.enums.EnumAmountUpDown;
import com.pro.framework.api.structure.Tuple2;
import com.pro.framework.api.util.AssertUtil;
import com.pro.framework.api.util.CollUtils;
import com.pro.framework.api.util.JSONUtils;
import com.pro.framework.mtq.service.multiwrapper.entity.IMultiPageResult;
import com.pro.framework.mybatisplus.BaseService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 文件节点 服务实现类
 */
@Service
@AllArgsConstructor
@Slf4j
public class UserFileNodeService extends BaseService<UserFileNodeDao, UserFileNode> {

    private UserFileService userFileService;
    private IFileStorageService fileStorageService;
    private UserAmountFeignClient userAmountFeignClient;
    private UserBatchInfoFeignClient userBatchInfoFeignClient;

    @Override
    public IMultiPageResult<UserFileNode> selectPage(String entityClassNames, IPageInput pageInput, Map<String, Object> paramMap, TimeQuery timeQuery) {
        Object parentId = paramMap.get("parentId");
        Object searchALl = paramMap.get("searchAll");
        if ((null == parentId || FrameworkConst.Str.EMPTY.equals(parentId)) && null == searchALl) {
            paramMap.put("parentId", 0L);
        }
        IMultiPageResult<UserFileNode> page = super.selectPage(entityClassNames, pageInput, paramMap, timeQuery);
        userFileService.fillUrl(fileStorageService, page.getRecords());
        return page;
    }

    public List<UserFileNode> listByUserAndPaths(Long userId, List<String> allPaths) {
        return this.lambdaQuery().eq(UserFileNode::getUserId, userId).in(UserFileNode::getLogicalPath, allPaths).list();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeById(Class<?> clazz, Serializable id) {
        UserFileNode fileNode = getById(id);
        if (fileNode != null) {
            switch (fileNode.getType()) {
                case FOLDER -> {
                    // 下面所有文件,文件夹(包括递归层次的)
                    List<UserFileNode> simpleList = this.lambdaQuery()
                            .select(
                                    UserFileNode::getId,
                                    UserFileNode::getType,
                                    UserFileNode::getSizeBytes,
                                    UserFileNode::getUserId
                            )
                            .likeRight(UserFileNode::getLogicalPath, fileNode.getLogicalPath()).list();
                    CollUtils.groupBy(simpleList, UserFileNode::getType).forEach((fileNodeType, userFileNodes) -> {
                        switch (fileNodeType) {
                            case FOLDER -> this.removeBatchByIds(userFileNodes.stream().map(BaseModel::getId).toList());
                            case FILE -> this.removeBatchFile(userFileNodes);
                        }
                    });
                }
                case FILE -> this.removeBatchFile(List.of(fileNode));
            }
            return super.removeById(clazz, id);
        }
        return true;
    }

    public static final String BUSINESS_TYPE = CommonConst.BusinessType.UserFile.UserFileRemove;

    private void removeBatchFile(List<UserFileNode> userFileNodeSimples) {
        List<Long> userFileNodeIds = userFileNodeSimples.stream().map(UserFileNode::getId).toList();
        Map<Long, UserFile> userFileMap = userFileService.getMap(userFileNodeIds);
        CollUtils.groupBy(userFileMap.values(), UserFile::getBucket, list -> list.stream().map(UserFile::getStorageKey).toList())
                // oss 删除
                .forEach((bucket, subList) -> fileStorageService.deleteObjects(bucket, subList));


        // 1.保存批次
        Map<Long, List<UserFileNode>> requestMap = CollUtils.groupBy(userFileNodeSimples, UserFileNode::getUserId);
        List<Tuple2<UserBatchInfo, List<UserFileNode>>> tuple2s = requestMap.entrySet().stream().map(e -> {
            Long userId = e.getKey();
            List<UserFileNode> subItems = e.getValue();
            UserBatchInfo userBatchInfo = new UserBatchInfo(userId, BUSINESS_TYPE, subItems.stream().map(i -> {
                UserBatchDetail batchDetail = new UserBatchDetail();
                batchDetail.setRecordId(i.getId());
                return batchDetail;
            }).toList());
            return new Tuple2<>(userBatchInfo, subItems);
        }).toList();
        userBatchInfoFeignClient.saveBatchAndFillId(tuple2s.stream().map(Tuple2::getT1).toList());

        // 图片空间退还
        List<AmountEntityRecordBaseDTO> amountChangeDTOs = tuple2s.stream().map(tuple2 -> {
            UserBatchInfo batchInfo = tuple2.getT1();
            List<UserFileNode> subItems = tuple2.getT2();
            long allSize = subItems.stream().map(UserFileNode::getSizeBytes).filter(Objects::nonNull).reduce(Long::sum).orElse(0L);
            if (allSize <= 0) {
                return null;
            }
            return (AmountEntityRecordBaseDTO) AmountEntityRecordBaseDTO.builder()
                    .userId(batchInfo.getUserId())
                    .amountType(CommonConst.BusinessType.UserAmount.AmountType.userFileStorage)
                    .upDown(EnumAmountUpDown.down)
                    .amount(new BigDecimal(allSize))
                    .recordType(BUSINESS_TYPE)
                    .orderId(batchInfo.getId())
                    .build();
        }).filter(Objects::nonNull).toList();
        List<UserAmountRecord> records = userAmountFeignClient.change(new AmountEntityUnitDTO<>(EnumAmountNegativeDeal.toZero, null, amountChangeDTOs, true));
        for (UserAmountRecord record : records) {
            if (!record.getSuccessFlag()) {
                log.warn("删除图片_图片空间退回失败 {}", JSONUtil.toJsonStr(record));
            }
        }
        super.removeBatchByIds(userFileNodeIds);
        userFileService.removeBatchByIds(userFileMap.values().stream().map(UserFile::getId).toList());

    }
}
